/*
Copyright (c) 2010, Pierre KRIEGER
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef INCLUDE_LUA_LUACONTEXT_H
#define INCLUDE_LUA_LUACONTEXT_H

#include <algorithm>
#include <cassert>
#include <cstring>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <random>
#include <set>
#include <stdexcept>
#include <string>
#include <sstream>
#include <tuple>
#include <type_traits>

extern "C" {
#	include "..\lualib\lua.h"
#	include "..\lualib\lualib.h"
#	include "..\lualib\lauxlib.h"
}

#if defined(__GNUC__) && __GNUC__ <= 4 && __GNUC_MINOR__ <= 5
#	define nullptr		0
#endif

#if defined(_MSC_VER) && _MSC_VER < 1700
#	define NO_VARIADIC_TEMPLATE
#endif


namespace Lua {
	/**	\brief Defines a Lua context
		\details A Lua context is used to interpret Lua code. Since everything in Lua is a variable (including functions),
				we only provide few functions like readVariable and writeVariable. Note that these functions can visit arrays,
				ie. calling readVariable("a.b.c") will read variable "c" from array "b", which is itself located in array "a".

				You can also write variables with C++ functions so that they are callable by Lua. Note however that you HAVE TO convert
				your function to std::function (not directly std::bind or a lambda function) so the class can detect which argument types
				it wants. These arguments may only be of basic types (int, float, etc.) or std::string.
	*/
	class LuaContext {
	public:
		explicit LuaContext(bool openDefaultLibs = true);
				 LuaContext(LuaContext&& s) : _state(s._state) { s._state = nullptr; }
				 LuaContext& operator=(LuaContext&& s) { std::swap(_state, s._state); return *this; }
				~LuaContext()				{ if (_state != nullptr) lua_close(_state); }
		

		/// \brief The table type can store any key and any value, and can be read or written by LuaContext
		class Table;

		/// \brief Thrown when an error happens during execution (like not enough parameters for a function)
		class ExecutionErrorException : public std::runtime_error { public: ExecutionErrorException(const std::string& msg) : std::runtime_error(msg.c_str()) {} };
		/// \brief Generated by readVariable or isVariableArray when the asked variable doesn't exist/is nil
		class VariableDoesntExistException : public std::runtime_error { public: VariableDoesntExistException(const std::string& variable) : std::runtime_error((std::string("Variable \"") + variable + std::string("\" doesn't exist in lua context")).c_str()) {} };
		/// \brief Thrown when a syntax error happens in a lua script
		class SyntaxErrorException : public std::runtime_error { public: SyntaxErrorException(const std::string& msg) : std::runtime_error(msg.c_str()) {} };
		/// \brief Thrown when trying to cast a lua variable to an unvalid type
		class WrongTypeException : public std::runtime_error { public: WrongTypeException() : std::runtime_error("Trying to cast a lua variable to an unvalid type") { } };

		
		/// \brief Executes lua code from the stream \param code A stream that lua will read its code from
		void		executeCode(std::istream& code)						{ _load(code); _call<std::tuple<>>(std::tuple<>()); }
		/// \brief Executes lua code from the stream and returns a value \param code A stream that lua will read its code from
		template<typename T>
		T			executeCode(std::istream& code)						{ _load(code); return _call<T>(std::tuple<>()); }
		/// \brief Executes lua code given as parameter \param code A string containing code that will be executed by lua
		void		executeCode(const std::string& code)				{ std::istringstream str(code); executeCode<void>(str); }
		/// \brief Executes lua code given as parameter and returns a value \param code A string containing code that will be executed by lua
		template<typename T>
		T			executeCode(const std::string& code)				{ std::istringstream str(code); return executeCode<T>(str); }
		

		/// \brief Tells that lua will be allowed to access an object's function
#		ifdef NO_VARIADIC_TEMPLATE
			template<typename T, typename R>
			void				registerFunction(const std::string& name, R (T::*f)())					{ _registerFunction(name, [f](std::shared_ptr<T> ptr) { return ((*ptr).*f)(); }); }
			template<typename T, typename R>
			void				registerFunction(const std::string& name, R (T::*f)() const)					{ _registerFunction(name, [f](std::shared_ptr<T> ptr) { return ((*ptr).*f)(); }); }
			template<typename T, typename R, typename P1>
			void				registerFunction(const std::string& name, R (T::*f)(P1))					{ _registerFunction(name, [f](std::shared_ptr<T> ptr, P1 p1) { return ((*ptr).*f)(p1); }); }
			template<typename T, typename R, typename P1>
			void				registerFunction(const std::string& name, R (T::*f)(P1) const)					{ _registerFunction(name, [f](std::shared_ptr<T> ptr, P1 p1) { return ((*ptr).*f)(p1); }); }
			template<typename T, typename R, typename P1, typename P2>
			void				registerFunction(const std::string& name, R (T::*f)(P1,P2))					{ _registerFunction(name, [f](std::shared_ptr<T> ptr, P1 p1, P2 p2) { return ((*ptr).*f)(p1,p2); }); }
			template<typename T, typename R, typename P1, typename P2>
			void				registerFunction(const std::string& name, R (T::*f)(P1,P2) const)					{ _registerFunction(name, [f](std::shared_ptr<T> ptr, P1 p1, P2 p2) { return ((*ptr).*f)(p1,p2); }); }
			template<typename T, typename R, typename P1, typename P2, typename P3>
			void				registerFunction(const std::string& name, R (T::*f)(P1,P2,P3))					{ _registerFunction(name, [f](std::shared_ptr<T> ptr, P1 p1, P2 p2, P3 p3) { return ((*ptr).*f)(p1,p2,p3); }); }
			template<typename T, typename R, typename P1, typename P2, typename P3, typename P4>
			void				registerFunction(const std::string& name, R (T::*f)(P1,P2,P3,P4))					{ _registerFunction(name, [f](std::shared_ptr<T> ptr, P1 p1, P2 p2, P3 p3, P4 p4) { return ((*ptr).*f)(p1,p2,p3,p4); }); }
#		else
			template<typename T, typename R, typename... Args>
			void				registerFunction(const std::string& name, R (T::*f)(Args...))						{ _registerFunction(name, [f](std::shared_ptr<T> ptr, Args... args) { return ((*ptr).*f)(args...); }); }
			template<typename T, typename R, typename... Args>
			void				registerFunction(const std::string& name, R (T::*f)(Args...) const)					{ _registerFunction(name, [f](std::shared_ptr<T> ptr, Args... args) { return ((*ptr).*f)(args...); }); }
			template<typename T, typename R, typename... Args>
			void				registerFunction(const std::string& name, R (T::*f)(Args...) volatile)				{ _registerFunction(name, [f](std::shared_ptr<T> ptr, Args... args) { return ((*ptr).*f)(args...); }); }
			template<typename T, typename R, typename... Args>
			void				registerFunction(const std::string& name, R (T::*f)(Args...) const volatile)		{ _registerFunction(name, [f](std::shared_ptr<T> ptr, Args... args) { return ((*ptr).*f)(args...); }); }
#		endif


		/// \brief Adds a custom function to a type determined using the function's first parameter
		/// \sa allowFunction
		/// \param fn Function which takes as first parameter a std::shared_ptr
		template<typename T>
		void				registerFunction(const std::string& name, T fn, decltype(&T::operator())* = nullptr)				{ _registerFunction(name, fn); }

		/// \brief Inverse operation of registerFunction
		template<typename T>
		void				unregisterFunction(const std::string& name)										{ _unregisterFunction<T>(name); }
		/// \brief Calls a function stored in a lua variable
		/// \details Template parameter of the function should be the expected return type (tuples and void are supported)
		/// \param variableName Name of the variable containing the function to call
		/// \param ... Parameters to pass to the function
#		ifdef NO_VARIADIC_TEMPLATE
			template<typename R>
			R callLuaFunction(const std::string& variableName) {
				_getGlobal(variableName);
				return _call<R>(std::tuple<>());
			}
			template<typename R, typename T1>
			R callLuaFunction(const std::string& variableName, const T1& t1) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1));
			}
			template<typename R, typename T1, typename T2>
			R callLuaFunction(const std::string& variableName, const T1& t1, const T2& t2) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1,t2));
			}
			template<typename R, typename T1, typename T2, typename T3>
			R callLuaFunction(const std::string& variableName, const T1& t1, const T2& t2, const T3& t3) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1,t2,t3));
			}
			template<typename R, typename T1, typename T2, typename T3, typename T4>
			R callLuaFunction(const std::string& variableName, const T1& t1, const T2& t2, const T3& t3, const T4& t4) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1,t2,t3,t4));
			}
			template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5>
			R callLuaFunction(const std::string& variableName, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1,t2,t3,t4,t5));
			}
			template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
			R callLuaFunction(const std::string& variableName, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1,t2,t3,t4,t5,t6));
			}
			template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
			R callLuaFunction(const std::string& variableName, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1,t2,t3,t4,t5,t6,t7));
			}
			template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
			R callLuaFunction(const std::string& variableName, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7, const T8& t8) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1,t2,t3,t4,t5,t6,t7,t8));
			}
			template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
			R callLuaFunction(const std::string& variableName, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7, const T8& t8, const T9& t9) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(t1,t2,t3,t4,t5,t6,t7,t8,t9));
			}
#		else
			template<typename R, typename... Args>
			R callLuaFunction(const std::string& variableName, const Args&... args) {
				_getGlobal(variableName);
				return _call<R>(std::make_tuple(args...));
			}
#		endif
		

		/// \brief Returns true if the value of the variable is an array \param variableName Name of the variable to check
		bool				isVariableArray(const std::string& variableName) const;

		/// \brief Writes an empty array into the given variable \note To write something in the array, use writeVariable. Example: writeArrayIntoVariable("myArr"); writeVariable("myArr.something", 5);
		void				writeArrayIntoVariable(const std::string& variableName);

		/// \brief Returns true if variable exists (ie. not nil)
		bool				doesVariableExist(const std::string& variableName) const			{ _getGlobal(variableName); bool answer = lua_isnil(_state, -1); lua_pop(_state, 1); return answer; }

		/// \brief Destroys a variable \details Puts the nil value into it
		void				clearVariable(const std::string& variableName)						{ lua_pushnil(_state); _setGlobal(variableName); }
		
		/// \brief Returns the content of a variable \throw VariableDoesntExistException if variable doesn't exist \note If you wrote a ObjectWrapper<T> into a variable, you can only read its value using a std::shared_ptr<T>
		template<typename T> T		readVariable(const std::string& variableName) const					{ _getGlobal(variableName); return _readTopAndPop(1, (T*)nullptr); }
		/// \brief
		template<typename T> bool 	readVariableIfExists(const std::string& variableName, T& out)		{ if (!doesVariableExist(variableName)) return false; out = readVariable<T>(variableName); return true; }

		/// \brief Changes the content of a global lua variable
		/// \details Accepted values are: all base types (integers, floats), std::string, std::function or ObjectWrapper<...>. All objects are passed by copy and destroyed by the garbage collector.
		template<typename T>
		void writeVariable(const std::string& variableName, T&& data) {
			static_assert(!std::is_same<typename Tupleizer<T>::type,T>::value, "Error: you can't use LuaContext::writeVariable with a tuple");
			const int pushedElems = _push(std::forward<T>(data));
			try { _setGlobal(variableName); } catch(...) { lua_pop(_state, pushedElems - 1); throw; }
			lua_pop(_state, pushedElems - 1);
		}



	private:
		// forbidding copy
		LuaContext(const LuaContext&);
		LuaContext& operator=(const LuaContext&);


		// the state is the most important variable in the class since it is our interface with Lua
		// the mutex is here because the lua design is not thread safe (based on a stack)
		//   eg. if multiple thread call "writeVariable" at the same time, we don't want them to be executed simultaneously
		// the mutex should be locked by all public functions that use the stack
		lua_State*					_state;
		
		// all the user types in the _state must have the value of &typeid(T) in their
		//   metatable at key "_typeid"

		// the getGlobal function is equivalent to lua_getglobal, except that it can interpret
		//   any variable name in a table form (ie. names like table.value are supported)
		// see also http://www.lua.org/manual/5.1/manual.html#lua_getglobal
		// same for setGlobal <=> lua_setglobal
		// important: _setGlobal will pop the value even if it throws an exception, while _getGlobal won't push the value if it throws an exception
		void	_getGlobal(const std::string& variable) const;
		void	_setGlobal(const std::string& variable);

		// simple function that reads the top # elements of the stack, pops them, and returns them
		// warning: first parameter is the number of parameters, not the parameter index
		// if _read generates an exception, stack is poped anyway
		template<typename R>
		typename std::enable_if<!std::is_void<R>::value,R>::type _readTopAndPop(int nb, R* ptr = nullptr) const {
			try {
				const R value = _read(-nb, ptr);
				lua_pop(_state, nb);
				return value;
			} catch(...) {
				lua_pop(_state, nb);
				throw;
			}
		}
		void _readTopAndPop(int nb, void* ptr = nullptr) const {
			lua_pop(_state, nb);
		}
		

		/**************************************************/
		/*            FUNCTIONS REGISTRATION              */
		/**************************************************/
		// the "registerFunction" public functions call this one
		// this function writes in registry the list of functions for each possible custom type (ie. T when pushing std::shared_ptr<T>)
		// to be clear, registry[&typeid(type)] contains an array where keys are function names and values are functions
		//		(where type is the first parameter of the functor)
		template<typename T>
		void _registerFunction(const std::string& name, T function) {
			typedef typename RemoveMemberPtr<decltype(&T::operator())>::type													FunctionType;
			typedef typename std::tuple_element<0,typename FnTupleWrapper<FunctionType>::ParamsType>::type::element_type		ObjectType;

			// trying to get the existing functions list
			lua_pushlightuserdata(_state, const_cast<std::type_info*>(&typeid(ObjectType)));
			lua_gettable(_state, LUA_REGISTRYINDEX);

			// if it doesn't exist, we create one, then write it in registry but keep it pushed
			if (!lua_istable(_state, -1)) {
				assert(lua_isnil(_state, -1));
				lua_pop(_state, 1);
				lua_newtable(_state);
				lua_pushlightuserdata(_state, const_cast<std::type_info*>(&typeid(ObjectType)));
				lua_pushvalue(_state, -2);
				lua_settable(_state, LUA_REGISTRYINDEX);
			}

			// now we have our functions list on top of the stack, we write the function here
			lua_pushstring(_state, name.c_str());
			_push(std::move(function));
			lua_settable(_state, -3);

			lua_pop(_state, 1);
		}

		// inverse operation of _registerFunction
		template<typename T> void _unregisterFunction(const std::string& name) {
			// trying to get the existing functions list
			lua_pushlightuserdata(_state, const_cast<std::type_info*>(&typeid(T)));
			lua_gettable(_state, LUA_REGISTRYINDEX);
			if (!lua_istable(_state, -1))	{ lua_pop(_state, -1); return; }
			lua_pushstring(_state, name.c_str());
			lua_pushnil(_state);
			lua_settable(_state, -3);
			lua_pop(_state, 1);
		}
		

		/**************************************************/
		/*              LOADING AND CALLING               */
		/**************************************************/
		// this function loads data from the stream and pushes a function at the top of the stack
		// it is defined in the .cpp
		void _load(std::istream& code);

		// this function calls what is on the top of the stack and removes it (just like lua_call)
		// if an exception is triggered, the top of the stack will be removed anyway
		// In should be a tuple (at least until variadic templates are supported everywhere), Out can be anything
		template<typename Out, typename In>
		Out _call(const In& in) {
			static_assert(std::tuple_size<In>::value >= 0, "Error: template parameter 'In' should be a tuple");

			int outArguments = 0;
			int inArguments = 0;
			try {
				// we push the parameters on the stack
				outArguments = std::tuple_size<typename Tupleizer<Out>::type>::value;
				inArguments = _push(in);
			} catch(...) { lua_pop(_state, 1); throw; }

			// calling pcall automatically pops the parameters and pushes output
			const auto pcallReturnValue = lua_pcall(_state, inArguments, outArguments, 0);

			// if pcall failed, analyzing the problem and throwing
			if (pcallReturnValue != 0) {
				// an error occured during execution, an error message was pushed on the stack
				const std::string errorMsg = _readTopAndPop(1, (std::string*)nullptr);
				if (pcallReturnValue == LUA_ERRMEM)			throw(std::bad_alloc());
				else if (pcallReturnValue == LUA_ERRRUN)	throw(ExecutionErrorException(errorMsg));
			}

			// pcall succeeded, we pop the returned values and return them
			try { return _readTopAndPop(outArguments, (Out*)nullptr);
			} catch(...) { lua_pop(_state, outArguments); throw; }
		}


		/**************************************************/
		/*                 TABLE CLASS                    */
		/**************************************************/
	public:
		class Table {
		public:
			 Table() {}
			 Table(Table&& t)				{ swap(t, *this); }
			 Table& operator=(Table&& t)	{ swap(t, *this); }
			~Table() {}

#			ifndef NO_VARIADIC_TEMPLATE
				template<typename... Args>
				explicit Table(Args... args)		{ insert(args...); }
#			endif

			friend void swap(Table& a, Table& b) {
				std::swap(a._elements, b._elements);
			}
			
#			ifndef NO_VARIADIC_TEMPLATE
				template<typename Key, typename Value, typename... Args>
				void insert(Key k, Value v, Args... args) {
					typedef typename ToPushableType<Key>::type RKey;
					typedef typename ToPushableType<Value>::type RValue;
					_elements.push_back(std::unique_ptr<ElementBase>(new Element<RKey,RValue>(std::move(k), std::move(v))));
					insert(args...);
				}

				void insert() {
				}
#			else
				template<typename Key, typename Value>
				void insert(Key k, Value v) {
					typedef typename ToPushableType<Key>::type RKey;
					typedef typename ToPushableType<Value>::type RValue;
					_elements.push_back(std::unique_ptr<ElementBase>(new Element<RKey,RValue>(std::move(k), std::move(v))));
				}
#			endif

			template<typename Value, typename Key>
			Value read(const Key& key) {
				typedef typename ToPushableType<Key>::type Key2;
				typedef typename ToPushableType<Value>::type Value2;
				for (auto i = _elements.rbegin(); i != _elements.rend(); ++i) {
					auto element = dynamic_cast<Element<Key2,Value2>*>(i->get());
					if (element != nullptr && element->key == key)
						return element->value;
				}
				throw(VariableDoesntExistException("<key in table>"));
			}

		private:
			Table(const Table&);
			Table& operator=(const Table&);

			// this is the base structure
			// the push function should add the key/value pair to the table currently at the top of the stack
			struct ElementBase {
				virtual ~ElementBase() {}
				virtual void push(LuaContext&) const = 0;
			};

			// derivate of ElementBase, real implementation
			template<typename Key, typename Value>
			struct Element : public ElementBase {
				 Element(Key k, Value v) : key(std::move(k)), value(std::move(v)) {}
				~Element() {}

				void push(LuaContext& ctxt) const {
					assert(lua_istable(ctxt._state, -1));
					ctxt._push(key);
					ctxt._push(value);
					lua_settable(ctxt._state, -3);
				}

				Key			key;
				Value		value;
			};

			// pushing the whole array
			friend class LuaContext;
			int _push(LuaContext& ctxt) const {
				lua_newtable(ctxt._state);
				try {
					for (auto i = _elements.begin(); i != _elements.end(); ++i)
						(*i)->push(ctxt);
				} catch(...) {
					lua_pop(ctxt._state, 1);
					throw;
				}
				return 1;
			}

			// elements storage
			std::list<std::unique_ptr<ElementBase>>			_elements;
		};
	private:



		/**************************************************/
		/*                PUSH FUNCTIONS                  */
		/**************************************************/
		// this structure converts an input type to a pushable output type
		template<typename Input, typename = void>
		struct ToPushableType;

		// first the basic ones: integer, number, boolean, string
		int _push()							{ return 0; }
		int _push(bool v)					{ lua_pushboolean(_state, v); return 1; }
		int _push(const std::string& s)		{ lua_pushstring(_state, s.c_str()); return 1; }
		int _push(const char* s)			{ lua_pushstring(_state, s); return 1; }
		
		// pushing floating numbers
		template<typename T>
		typename std::enable_if<std::is_floating_point<T>::value,int>::type _push(T nb) {
			lua_pushnumber(_state, nb);
			return 1;
		}
		
		// pushing integers
		template<typename T>
		typename std::enable_if<std::is_integral<T>::value,int>::type _push(T nb) {
			lua_pushinteger(_state, nb);
			return 1;
		}

		// using variadic templates, you can push multiple values at once
#		ifndef NO_VARIADIC_TEMPLATE
			template<typename T1, typename T2, typename... Args>
			int _push(T1&& v1, T2&& v2, Args&&... args) {
				int p = _push(std::forward<T1>(v1));
				try { p += _push(std::forward<T2>(v2), args...);
				} catch(...) { lua_pop(_state, p); throw; }
				return p;
			}
#		endif
		
		// pushing tables
		int _push(const Table& table) {
			return table._push(*this);
		}

		// pushing maps
		template<typename Key, typename Value>
		int _push(const std::map<Key,Value>& map) {
			lua_newtable(_state);

			for (auto i = map.begin(); i != map.end(); ++i) {
				_push(i->first);
				_push(i->second);
				lua_settable(_state, -3);
			}

			return 1;
		}
		
		// when you call _push with a functor, this definition should be used (thanks to SFINAE)
		// it will determine the function category using its () operator, then
		//   generate a callable user data and push it
		template<typename T>
		int _push(T fn, decltype(&T::operator()) = nullptr) {
			// the () operator has type "R (T::*)(Args)", this typedef converts it to "R (Args)"
			typedef typename RemoveMemberPtr<decltype(&T::operator())>::type		FnType;

			// when the lua script calls the thing we will push on the stack, we want "fn" to be executed
			// if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems
			// so we use userdata instead

			// we will create a userdata which contains a copy of a lambda function [](lua_State*) -> int
			// but first we have to create it
			auto functionToPush = [this,fn](lua_State* state) -> int {
				// note that I'm using "this->" because of g++,
				//	I don't know if it is required by standards or if it is a bug
				assert(this->_state == state);

				// FnTupleWrapper<FnType> is a specialized template structure which defines
				// "ParamsType", "ReturnType" and "call"
				// the first two correspond to the params list and return type as tuples
				//   and "call" is a static function which will call a function
				//   of this type using parameters passed as a tuple
				typedef	LuaContext::FnTupleWrapper<FnType>		TupledFunction;
				
				// checking if number of parameters is correct
				const int paramsCount = std::tuple_size<typename TupledFunction::ParamsType>::value;
				if (lua_gettop(state) < paramsCount) {
					// if not, using lua_error to return an error
					luaL_where(state, 1);
					lua_pushstring(state, "this function requires at least ");
					lua_pushnumber(state, paramsCount);
					lua_pushstring(state, " parameter(s)");
					lua_concat(state, 4);

					// lua_error throws an exception when compiling as C++
					return lua_error(state);
				}
				
				// reading parameters from the stack
#				ifdef _MSC_VER
					auto parameters = this->_read(-paramsCount, static_cast<TupledFunction::ParamsType*>(nullptr));
#				else
					auto parameters = this->_read(-paramsCount, static_cast<typename TupledFunction::ParamsType*>(nullptr));
#				endif
				// calling the function, note that "result" should be a tuple
				auto result = TupledFunction::call(fn, std::move(parameters));
				// pushing the result on the stack and returning number of pushed elements
				return this->_push(std::move(result));
			};
			
			// typedefing the type of data we will push
			typedef decltype(functionToPush)	FunctionPushType;
			
			// this is a structure providing static C-like functions that we can feed to lua
			// TODO: with full C++0x, these can be replaced by lambda functions with
			//   nothing inside the brackets []
			struct Callback {
				// this function is called when the lua script tries to call our custom data type
				// what we do is we simply call the function
				static int call(lua_State* lua) {
					assert(lua_gettop(lua) >= 1);
					assert(lua_isuserdata(lua, 1));
					FunctionPushType* function = (FunctionPushType*)lua_touserdata(lua, 1);
					assert(function);
					return (*function)(lua);
				}

				// this one is called when lua's garbage collector no longer needs our custom data type
				// we call std::function<int (lua_State*)>'s destructor
				static int garbage(lua_State* lua) {
					assert(lua_gettop(lua) == 1);
					FunctionPushType* function = (FunctionPushType*)lua_touserdata(lua, 1);
					assert(function);
					function->~FunctionPushType();
					return 0;
				}
			};

			// creating the object
			// lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
			//   and that's what we do with placement-new
			FunctionPushType* const functionLocation = (FunctionPushType*)lua_newuserdata(_state, sizeof(FunctionPushType));
			new (functionLocation) FunctionPushType(std::move(functionToPush));

			// creating the metatable (over the object on the stack)
			// lua_settable pops the key and value we just pushed, so stack management is easy
			// all that remains on the stack after these function calls is the metatable
			lua_newtable(_state);
			lua_pushstring(_state, "__call");
			lua_pushcfunction(_state, &Callback::call);
			lua_settable(_state, -3);
			lua_pushstring(_state, "_typeid");
			lua_pushlightuserdata(_state, const_cast<std::type_info*>(&typeid(T)));
			lua_settable(_state, -3);
			lua_pushstring(_state, "__gc");
			lua_pushcfunction(_state, &Callback::garbage);
			lua_settable(_state, -3);

			// at this point, the stack contains the object at offset -2 and the metatable at offset -1
			// lua_setmetatable will bind the two together and pop the metatable
			// our custom function remains on the stack (and that's what we want)
			lua_setmetatable(_state, -2);

			return 1;
		}

		// when pushing a unique_ptr, it is simply converted to a shared_ptr
		// this definition is necessary because unique_ptr has an implicit bool conversion operator
		// with C++0x, this bool operator will certainly be declared explicit
		template<typename T>
		int _push(std::unique_ptr<T> obj) {
			return _push(std::shared_ptr<T>(std::move(obj)));
		}

		// when pushing a shared_ptr, we create a custom type
		// we store a copy of the shared_ptr inside lua's internals
		//   and add it a metatable: __gc for destruction and __index pointing to the corresponding
		//   table in the registry (see _registerFunction)
		template<typename T>
		int _push(std::shared_ptr<T> obj) {
			// this is a structure providing static C-like functions that we can feed to lua
			struct Callback {
				// this function is called when lua's garbage collector no longer needs our shared_ptr
				// we simply call its destructor
				static int garbage(lua_State* lua) {
					assert(lua_gettop(lua) == 1);
					std::shared_ptr<T>* ptr = (std::shared_ptr<T>*)lua_touserdata(lua, 1);
					assert(ptr && *ptr);
					ptr->~shared_ptr();
					return 0;
				}
			};

			// creating the object
			// lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it
			//   and that's what we do with placement-new
			std::shared_ptr<T>* const pointerLocation = (std::shared_ptr<T>*)lua_newuserdata(_state, sizeof(std::shared_ptr<T>));
			try {
				new (pointerLocation) std::shared_ptr<T>(std::move(obj));

				// creating the metatable (over the object on the stack)
				// lua_settable pops the key and value we just pushed, so stack management is easy
				// all that remains on the stack after these function calls is the metatable
				lua_newtable(_state);
				try {
					// using the garbage collecting function we created above
					lua_pushstring(_state, "__gc");
					lua_pushcfunction(_state, &Callback::garbage);
					lua_settable(_state, -3);

					// settings typeid of shared_ptr this time
					lua_pushstring(_state, "_typeid");
					lua_pushlightuserdata(_state, const_cast<std::type_info*>(&typeid(std::shared_ptr<T>)));
					lua_settable(_state, -3);

					// as __index we set the table located in registry at type name
					// see comments at _registerFunction
					lua_pushstring(_state, "__index");
					lua_pushlightuserdata(_state, const_cast<std::type_info*>(&typeid(T)));
					lua_gettable(_state, LUA_REGISTRYINDEX);
					if (!lua_istable(_state, -1)) {
						assert(lua_isnil(_state, -1));
						lua_pop(_state, 1);
						lua_newtable(_state);
						lua_pushlightuserdata(_state, const_cast<std::type_info*>(&typeid(T)));
						lua_pushvalue(_state, -2);
						lua_settable(_state, LUA_REGISTRYINDEX);
					}
					lua_settable(_state, -3);
					
					// at this point, the stack contains the object at offset -2 and the metatable at offset -1
					// lua_setmetatable will bind the two together and pop the metatable
					// our custom type remains on the stack (and that's what we want since this is a push function)
					lua_setmetatable(_state, -2);

				} catch(...) { lua_pop(_state, 1); throw; }
			} catch(...) { lua_pop(_state, 1); throw; }

			return 1;
		}
		
		// pushing tuples is also possible, though a bit complicated
#		ifndef NO_VARIADIC_TEMPLATE
			template<typename... Args, int N = sizeof...(Args)>
			int _push(const std::tuple<Args...>& t, std::integral_constant<int,N>* = nullptr, typename std::enable_if<(N >= 1)>::type* = nullptr) {
				return _push(t, static_cast<std::integral_constant<int,N-1>*>(nullptr)) + _push(std::get<N-1>(t));
			}
			template<typename... Args, int N = sizeof...(Args)>
			int _push(const std::tuple<Args...>& t, std::integral_constant<int,N>* = nullptr, typename std::enable_if<(N == 0)>::type* = nullptr) {
				return 0;
			}
#		else
			int _push(std::tuple<>) {
				return 0;
			}
			template<typename T1>
			int _push(std::tuple<T1> t) {
				return _push(std::move(std::get<0>(t)));
			}
			template<typename T1, typename T2>
			int _push(std::tuple<T1,T2> t) {
				int p = _push(std::move(std::get<0>(t)));
				p += _push(std::move(std::get<1>(t)));
				return p;
			}
			template<typename T1, typename T2, typename T3>
			int _push(std::tuple<T1,T2,T3> t) {
				int p = _push(std::move(std::get<0>(t)));
				p += _push(std::move(std::get<1>(t)));
				p += _push(std::move(std::get<2>(t)));
				return p;
			}
			template<typename T1, typename T2, typename T3, typename T4>
			int _push(std::tuple<T1,T2,T3,T4> t) {
				int p = _push(std::move(std::get<0>(t)));
				p += _push(std::move(std::get<1>(t)));
				p += _push(std::move(std::get<2>(t)));
				p += _push(std::move(std::get<3>(t)));
				return p;
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5>
			int _push(std::tuple<T1,T2,T3,T4,T5> t) {
				int p = _push(std::move(std::get<0>(t)));
				p += _push(std::move(std::get<1>(t)));
				p += _push(std::move(std::get<2>(t)));
				p += _push(std::move(std::get<3>(t)));
				p += _push(std::move(std::get<4>(t)));
				return p;
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
			int _push(std::tuple<T1,T2,T3,T4,T5,T6> t) {
				int p = _push(std::move(std::get<0>(t)));
				p += _push(std::move(std::get<1>(t)));
				p += _push(std::move(std::get<2>(t)));
				p += _push(std::move(std::get<3>(t)));
				p += _push(std::move(std::get<4>(t)));
				p += _push(std::move(std::get<5>(t)));
				return p;
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
			int _push(std::tuple<T1,T2,T3,T4,T5,T6,T7> t) {
				int p = _push(std::move(std::get<0>(t)));
				p += _push(std::move(std::get<1>(t)));
				p += _push(std::move(std::get<2>(t)));
				p += _push(std::move(std::get<3>(t)));
				p += _push(std::move(std::get<4>(t)));
				p += _push(std::move(std::get<5>(t)));
				p += _push(std::move(std::get<6>(t)));
				return p;
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
			int _push(std::tuple<T1,T2,T3,T4,T5,T6,T7,T8> t) {
				int p = _push(std::move(std::get<0>(t)));
				p += _push(std::move(std::get<1>(t)));
				p += _push(std::move(std::get<2>(t)));
				p += _push(std::move(std::get<3>(t)));
				p += _push(std::move(std::get<4>(t)));
				p += _push(std::move(std::get<5>(t)));
				p += _push(std::move(std::get<6>(t)));
				p += _push(std::move(std::get<7>(t)));
				return p;
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
			int _push(std::tuple<T1,T2,T3,T4,T5,T6,T7,T8,T9> t) {
				int p = _push(std::move(std::get<0>(t)));
				p += _push(std::move(std::get<1>(t)));
				p += _push(std::move(std::get<2>(t)));
				p += _push(std::move(std::get<3>(t)));
				p += _push(std::move(std::get<4>(t)));
				p += _push(std::move(std::get<5>(t)));
				p += _push(std::move(std::get<6>(t)));
				p += _push(std::move(std::get<7>(t)));
				p += _push(std::move(std::get<8>(t)));
				return p;
			}
#		endif
		

		/**************************************************/
		/*                READ FUNCTIONS                  */
		/**************************************************/
		// to use the _read function, pass as second parameter a null pointer whose base type is the wanted return type
		// eg. if you want an int, pass "static_cast<int*>(nullptr)" as second parameter

		// reading void
		void _read(int index, void const* = nullptr) const {
		}

		// first the integer types
		template<typename T>
		typename std::enable_if<std::numeric_limits<T>::is_integer,T>::type _read(int index, T const* = nullptr) const {
			if (lua_isuserdata(_state, index))
				throw(WrongTypeException());
			return T(lua_tointeger(_state, index));
		}

		// then the floating types
		template<typename T>
		typename std::enable_if<std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_integer,T>::type
				_read(int index, T const* = nullptr) const {

			if (lua_isuserdata(_state, index))
				throw(WrongTypeException());
			return T(lua_tonumber(_state, index));
		}

		// boolean
		bool _read(int index, bool const* = nullptr) const {
			if (lua_isuserdata(_state, index))
				throw(WrongTypeException());
			return lua_toboolean(_state, index) != 0;		// "!= 0" removes a warning because lua_toboolean returns an int
		}

		// string
		// lua_tostring returns a temporary pointer, but that's not a problem since we copy
		//   the data in a std::string
		std::string _read(int index, std::string const* = nullptr) const {
			if (lua_isuserdata(_state, index))
				throw(WrongTypeException());
			return lua_tostring(_state, index);
		}

		// maps
		template<typename Key, typename Value>
		std::map<Key,Value> _read(int index, std::map<Key,Value> const* = nullptr) const {
			if (!lua_istable(_state, index))
				throw(WrongTypeException());


			std::map<Key,Value> retValue;

			// we traverse the table at the top of the stack
			lua_pushnil(_state);		// first key
			while (lua_next(_state, index - 1) != 0) {
				// now a key and its value are pushed on the stack
				retValue.insert(std::make_pair(_read(-2, static_cast<Key*>(nullptr)), _read(-2, static_cast<Value*>(nullptr))));
				lua_pop(_state, 1);		// we remove the value but keep the key for the next iteration
			}

			return retValue;
		}

		// reading array
		Table _read(int index, Table const* = nullptr) const {
			if (!lua_istable(_state, index))
				throw(WrongTypeException());

			throw(std::logic_error("Not implemented"));

			/*Table table;

			// we traverse the table at the top of the stack
			lua_pushnil(_state);		// first key
			while (lua_next(_state, -2) != 0) {
				// now a key and its value are pushed on the stack
				auto keyType = lua_type(_state, -2);
				auto valueType = lua_type(_state, -1);

				switch (keyType) {
					case LUA_TNUMBER:			break;
					case LUA_TBOOLEAN:			break;
					case LUA_TSTRING:			break;
					case LUA_TTABLE:			break;
					case LUA_TFUNCTION:			break;
					case LUA_TUSERDATA:			break;
					case LUA_TLIGHTUSERDATA:	break;
					default:		throw(WrongTypeException());
				}

				lua_pop(_state, 1);		// we remove the value but keep the key for the next iteration
			}

			return table;*/
		}

		// reading a shared_ptr
		// we check that type is correct by reading the metatable
		template<typename T>
		std::shared_ptr<T> _read(int index, std::shared_ptr<T> const* = nullptr) const {
			if (!lua_isuserdata(_state, index))		throw(WrongTypeException());
			if (!lua_getmetatable(_state, index))	throw(WrongTypeException());

			// now we have our metatable on the top of the stack
			// retrieving its _typeid member
			lua_pushstring(_state, "_typeid");
			lua_gettable(_state, -2);
			// if wrong typeid, we throw
			if (lua_touserdata(_state, -1) != const_cast<std::type_info*>(&typeid(std::shared_ptr<T>))) {
				lua_pop(_state, 2);
				throw(WrongTypeException());
			}
			lua_pop(_state, 2);

			// now we know that the type is correct, we retrieve the pointer
			const auto ptr = static_cast<std::shared_ptr<T>*>(lua_touserdata(_state, index));
			assert(ptr && *ptr);
			return *ptr;		// returning a copy of the shared_ptr
		}

		// reading a tuple
#		ifndef NO_VARIADIC_TEMPLATE
			template<typename First, typename... Args>
			std::tuple<First,Args...> _read(int index, std::tuple<First, Args...> const* = nullptr) const {
				return std::tuple_cat(std::make_tuple(_read(index, static_cast<First*>(nullptr))), _read(index + 1, static_cast<std::tuple<Args...>*>(nullptr)));
			}
			std::tuple<> _read(int index, std::tuple<> const* = nullptr) const {
				return std::tuple<>();
			}
#		else
			std::tuple<> _read(int index, std::tuple<> const* = nullptr) const {
				return std::tuple<>();
			}
			template<typename T1>
			std::tuple<T1> _read(int index, std::tuple<T1> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr));
			}
			template<typename T1, typename T2>
			std::tuple<T1,T2> _read(int index, std::tuple<T1,T2> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr), _read(index+1, (T2*)nullptr));
			}
			template<typename T1, typename T2, typename T3>
			std::tuple<T1,T2,T3> _read(int index, std::tuple<T1,T2,T3> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr), _read(index+1, (T2*)nullptr), _read(index+2, (T3*)nullptr));
			}
			template<typename T1, typename T2, typename T3, typename T4>
			std::tuple<T1,T2,T3,T4> _read(int index, std::tuple<T1,T2,T3,T4> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr), _read(index+1, (T2*)nullptr), _read(index+2, (T3*)nullptr), _read(index+3, (T4*)nullptr));
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5>
			std::tuple<T1,T2,T3,T4,T5> _read(int index, std::tuple<T1,T2,T3,T4,T5> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr), _read(index+1, (T2*)nullptr), _read(index+2, (T3*)nullptr), _read(index+3, (T4*)nullptr), _read(index+4, (T5*)nullptr));
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
			std::tuple<T1,T2,T3,T4,T5,T6> _read(int index, std::tuple<T1,T2,T3,T4,T5,T6> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr), _read(index+1, (T2*)nullptr), _read(index+2, (T3*)nullptr), _read(index+3, (T4*)nullptr), _read(index+4, (T5*)nullptr), _read(index+5, (T6*)nullptr));
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
			std::tuple<T1,T2,T3,T4,T5,T6,T7> _read(int index, std::tuple<T1,T2,T3,T4,T5,T6,T7> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr), _read(index+1, (T2*)nullptr), _read(index+2, (T3*)nullptr), _read(index+3, (T4*)nullptr), _read(index+4, (T5*)nullptr), _read(index+5, (T6*)nullptr), _read(index+6, (T7*)nullptr));
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
			std::tuple<T1,T2,T3,T4,T5,T6,T7,T8> _read(int index, std::tuple<T1,T2,T3,T4,T5,T6,T7,T8> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr), _read(index+1, (T2*)nullptr), _read(index+2, (T3*)nullptr), _read(index+3, (T4*)nullptr), _read(index+4, (T5*)nullptr), _read(index+5, (T6*)nullptr), _read(index+6, (T7*)nullptr), _read(index+7, (T8*)nullptr));
			}
			template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
			std::tuple<T1,T2,T3,T4,T5,T6,T7,T8,T9> _read(int index, std::tuple<T1,T2,T3,T4,T5,T6,T7,T8,T9> const* = nullptr) const {
				return std::make_tuple(_read(index, (T1*)nullptr), _read(index+1, (T2*)nullptr), _read(index+2, (T3*)nullptr), _read(index+3, (T4*)nullptr), _read(index+4, (T5*)nullptr), _read(index+5, (T6*)nullptr), _read(index+6, (T7*)nullptr), _read(index+7, (T8*)nullptr), _read(index+8, (T9*)nullptr));
			}
#		endif


		/**************************************************/
		/*                   UTILITIES                    */
		/**************************************************/
		template<typename FnType> struct FnTupleWrapper;
		template<typename T> struct Tupleizer;
		template<typename Fn> struct RemoveMemberPtr;
	};

	template<typename T>
	struct IsFunctor {
		typedef char one;
		typedef long two;

		template <typename C> static one test(decltype(&C::operator())) ;
		template <typename C> static two test(...);

		enum { value = sizeof(test<T>(0)) == sizeof(char) };
	};
	
	// you must be able to convert T to ToPushableType<T>::type
	template<typename T>	struct LuaContext::ToPushableType<T&>					{ typedef typename ToPushableType<T>::type type; };
	template<typename T>	struct LuaContext::ToPushableType<const T&>				{ typedef typename ToPushableType<T>::type type; };
	template<typename T>	struct LuaContext::ToPushableType<T, typename std::enable_if<std::is_integral<T>::value>::type> { typedef lua_Integer type; };
	template<typename T>	struct LuaContext::ToPushableType<T, typename std::enable_if<std::is_floating_point<T>::value>::type> { typedef lua_Number type; };
	template<>				struct LuaContext::ToPushableType<bool>					{ typedef bool type; };
	template<>				struct LuaContext::ToPushableType<const char*>			{ typedef std::string type; };
	template<int N>			struct LuaContext::ToPushableType<const char[N]>		{ typedef std::string type; };
	template<int N>			struct LuaContext::ToPushableType<char[N]>				{ typedef std::string type; };
	template<>				struct LuaContext::ToPushableType<std::string>			{ typedef std::string type; };
	template<typename T>	struct LuaContext::ToPushableType<std::unique_ptr<T>>	{ typedef std::shared_ptr<T> type; };
	template<typename T>	struct LuaContext::ToPushableType<std::shared_ptr<T>>	{ typedef std::shared_ptr<T> type; };
	template<>				struct LuaContext::ToPushableType<LuaContext::Table>	{ typedef LuaContext::Table type; };
	template<typename T>	struct LuaContext::ToPushableType<T, typename std::enable_if<IsFunctor<T>::value>::type> { typedef T type; };

	// this structure takes a function definition as template parameter and defines three things:
	// a ParamsType type which converts the function parameters into a tuple,
	// a ReturnType type which is either std::tuple<> (if void) or std::tuple<original return type>
	// a call function which calls the function using a tuple of type ParamsType, and returns ReturnType
	// this class only supports functions with up to 9 parameters, if you want more either add it yourself or wait for variadic templates
	template<typename FnType>
	struct LuaContext::FnTupleWrapper		{ };
	template<>
	struct LuaContext::FnTupleWrapper<void ()> {
		typedef std::tuple<>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, ParamsType params)		{ fn(); return ReturnType(); }
	};
	template<typename R>
	struct LuaContext::FnTupleWrapper<R ()> {
		typedef std::tuple<>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, ParamsType params)		{ return ReturnType(fn()); }
	};
	template<typename T1>
	struct LuaContext::FnTupleWrapper<void (T1)> {
		typedef typename LuaContext::Tupleizer<T1>::type	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, ParamsType params)	{ fn(std::move(std::get<0>(params))); return ReturnType(); }
	};
	template<typename R, typename T1>
	struct LuaContext::FnTupleWrapper<R (T1)> {
		typedef typename LuaContext::Tupleizer<T1>::type	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, ParamsType params)		{ return ReturnType(fn(std::move(std::get<0>(params)))); }
	};
	template<typename T1, typename T2>
	struct LuaContext::FnTupleWrapper<void (T1,T2)> {
		typedef std::tuple<T1,T2>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)	{ fn(std::get<0>(params), std::get<1>(params)); return ReturnType(); }
	};
	template<typename R, typename T1, typename T2>
	struct LuaContext::FnTupleWrapper<R (T1,T2)> {
		typedef std::tuple<T1,T2>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)		{ return ReturnType(fn(std::get<0>(params), std::get<1>(params))); }
	};
	template<typename T1, typename T2, typename T3>
	struct LuaContext::FnTupleWrapper<void (T1,T2,T3)> {
		typedef std::tuple<T1,T2,T3>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)	{ fn(std::get<0>(params), std::get<1>(params), std::get<2>(params)); return ReturnType(); }
	};
	template<typename R, typename T1, typename T2, typename T3>
	struct LuaContext::FnTupleWrapper<R (T1,T2,T3)> {
		typedef std::tuple<T1,T2,T3>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)		{ return ReturnType(fn(std::get<0>(params), std::get<1>(params), std::get<2>(params))); }
	};
	template<typename T1, typename T2, typename T3, typename T4>
	struct LuaContext::FnTupleWrapper<void (T1,T2,T3,T4)> {
		typedef std::tuple<T1,T2,T3,T4>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)	{ fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params)); return ReturnType(); }
	};
	template<typename R, typename T1, typename T2, typename T3, typename T4>
	struct LuaContext::FnTupleWrapper<R (T1,T2,T3,T4)> {
		typedef std::tuple<T1,T2,T3,T4>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)		{ return ReturnType(fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params))); }
	};
	template<typename T1, typename T2, typename T3, typename T4, typename T5>
	struct LuaContext::FnTupleWrapper<void (T1,T2,T3,T4,T5)> {
		typedef std::tuple<T1,T2,T3,T4,T5>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)	{ fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params)); return ReturnType(); }
	};
	template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5>
	struct LuaContext::FnTupleWrapper<R (T1,T2,T3,T4,T5)> {
		typedef std::tuple<T1,T2,T3,T4,T5>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)		{ return ReturnType(fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params))); }
	};
	template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
	struct LuaContext::FnTupleWrapper<void (T1,T2,T3,T4,T5,T6)> {
		typedef std::tuple<T1,T2,T3,T4,T5,T6>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)	{ fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params), std::get<5>(params)); return ReturnType(); }
	};
	template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
	struct LuaContext::FnTupleWrapper<R (T1,T2,T3,T4,T5,T6)> {
		typedef std::tuple<T1,T2,T3,T4,T5,T6>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)		{ return ReturnType(fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params), std::get<5>(params))); }
	};
	template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
	struct LuaContext::FnTupleWrapper<void (T1,T2,T3,T4,T5,T6,T7)> {
		typedef std::tuple<T1,T2,T3,T4,T5,T6,T7>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)	{ fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params), std::get<5>(params), std::get<6>(params)); return ReturnType(); }
	};
	template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
	struct LuaContext::FnTupleWrapper<R (T1,T2,T3,T4,T5,T6,T7)> {
		typedef std::tuple<T1,T2,T3,T4,T5,T6,T7>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)		{ return ReturnType(fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params), std::get<5>(params), std::get<6>(params))); }
	};
	template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
	struct LuaContext::FnTupleWrapper<void (T1,T2,T3,T4,T5,T6,T7,T8)> {
		typedef std::tuple<T1,T2,T3,T4,T5,T6,T7,T8>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)	{ fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params), std::get<5>(params), std::get<6>(params), std::get<7>(params)); return ReturnType(); }
	};
	template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
	struct LuaContext::FnTupleWrapper<R (T1,T2,T3,T4,T5,T6,T7,T8)> {
		typedef std::tuple<T1,T2,T3,T4,T5,T6,T7,T8>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)		{ return ReturnType(fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params), std::get<5>(params), std::get<6>(params), std::get<7>(params))); }
	};
	template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
	struct LuaContext::FnTupleWrapper<void (T1,T2,T3,T4,T5,T6,T7,T8,T9)> {
		typedef std::tuple<T1,T2,T3,T4,T5,T6,T7,T8,T9>	ParamsType;
		typedef std::tuple<>	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)	{ fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params), std::get<5>(params), std::get<6>(params), std::get<7>(params), std::get<8>(params)); return ReturnType(); }
	};
	template<typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
	struct LuaContext::FnTupleWrapper<R (T1,T2,T3,T4,T5,T6,T7,T8,T9)> {
		typedef std::tuple<T1,T2,T3,T4,T5,T6,T7,T8,T9>	ParamsType;
		typedef typename LuaContext::Tupleizer<R>::type	ReturnType;
		template<typename T>
		static ReturnType call(const T& fn, const ParamsType& params)		{ return ReturnType(fn(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params), std::get<4>(params), std::get<5>(params), std::get<6>(params), std::get<7>(params), std::get<8>(params))); }
	};


	// this structure takes a member function pointer and returns its base type
	// typically used on a functor T, like: std::function<RemoveMemberPtr<decltype(&T::operator())>::type>
#	ifndef NO_VARIADIC_TEMPLATE
		template<typename R, typename T, typename... Args>
		struct LuaContext::RemoveMemberPtr<R (T::*)(Args...)>							{ typedef R (type)(Args...); };
		template<typename R, typename T, typename... Args>
		struct LuaContext::RemoveMemberPtr<R (T::*)(Args...) const>						{ typedef R (type)(Args...); };
		template<typename R, typename T, typename... Args>
		struct LuaContext::RemoveMemberPtr<R (T::*)(Args...) volatile>					{ typedef R (type)(Args...); };
		template<typename R, typename T, typename... Args>
		struct LuaContext::RemoveMemberPtr<R (T::*)(Args...) const volatile>			{ typedef R (type)(Args...); };
#	else
		template<typename R, typename T>
		struct LuaContext::RemoveMemberPtr<R (T::*)()>									{ typedef R (type)(); };
		template<typename R, typename T>
		struct LuaContext::RemoveMemberPtr<R (T::*)() const>							{ typedef R (type)(); };
		template<typename R, typename T, typename P1>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1)>								{ typedef R (type)(P1); };
		template<typename R, typename T, typename P1>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1) const>							{ typedef R (type)(P1); };
		template<typename R, typename T, typename P1, typename P2>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2)>								{ typedef R (type)(P1,P2); };
		template<typename R, typename T, typename P1, typename P2>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2) const>						{ typedef R (type)(P1,P2); };
		template<typename R, typename T, typename P1, typename P2, typename P3>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3)>							{ typedef R (type)(P1,P2,P3); };
		template<typename R, typename T, typename P1, typename P2, typename P3>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3) const>					{ typedef R (type)(P1,P2,P3); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4)>						{ typedef R (type)(P1,P2,P3,P4); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4) const>					{ typedef R (type)(P1,P2,P3,P4); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5)>					{ typedef R (type)(P1,P2,P3,P4,P5); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5) const>				{ typedef R (type)(P1,P2,P3,P4,P5); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5,P6)>					{ typedef R (type)(P1,P2,P3,P4,P5,P6); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5,P6) const>			{ typedef R (type)(P1,P2,P3,P4,P5,P6); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5,P6,P7)>				{ typedef R (type)(P1,P2,P3,P4,P5,P6,P7); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5,P6,P7) const>		{ typedef R (type)(P1,P2,P3,P4,P5,P6,P7); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5,P6,P7,P8)>			{ typedef R (type)(P1,P2,P3,P4,P5,P6,P7,P8); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5,P6,P7,P8) const>		{ typedef R (type)(P1,P2,P3,P4,P5,P6,P7,P8); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5,P6,P7,P8,P9)>		{ typedef R (type)(P1,P2,P3,P4,P5,P6,P7,P8,P9); };
		template<typename R, typename T, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
		struct LuaContext::RemoveMemberPtr<R (T::*)(P1,P2,P3,P4,P5,P6,P7,P8,P9) const>	{ typedef R (type)(P1,P2,P3,P4,P5,P6,P7,P8,P9); };
#	endif


	// this structure takes a template parameter T
	// if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple<T>
	// you have to use this structure because std::tuple<std::tuple<...>> triggers a bug in both MSVC++ and GCC
#	ifndef NO_VARIADIC_TEMPLATE
		template<typename T> struct LuaContext::Tupleizer						{ typedef std::tuple<T> type; };
		template<typename... Args>
		struct LuaContext::Tupleizer<std::tuple<Args...>>						{ typedef std::tuple<Args...> type; };
		template<> struct LuaContext::Tupleizer<void>							{ typedef std::tuple<> type; };
#	else
		template<typename T> struct LuaContext::Tupleizer						{ typedef std::tuple<T> type; };
		template<> struct LuaContext::Tupleizer<void>							{ typedef std::tuple<> type; };
		template<> struct LuaContext::Tupleizer<std::tuple<>>					{ typedef std::tuple<> type; };
		template<typename T1>
		struct LuaContext::Tupleizer<std::tuple<T1>>							{ typedef std::tuple<T1> type; };
		template<typename T1, typename T2>
		struct LuaContext::Tupleizer<std::tuple<T1,T2>>							{ typedef std::tuple<T1,T2> type; };
		template<typename T1, typename T2, typename T3>
		struct LuaContext::Tupleizer<std::tuple<T1,T2,T3>>						{ typedef std::tuple<T1,T2,T3> type; };
		template<typename T1, typename T2, typename T3, typename T4>
		struct LuaContext::Tupleizer<std::tuple<T1,T2,T3,T4>>					{ typedef std::tuple<T1,T2,T3,T4> type; };
		template<typename T1, typename T2, typename T3, typename T4, typename T5>
		struct LuaContext::Tupleizer<std::tuple<T1,T2,T3,T4,T5>>				{ typedef std::tuple<T1,T2,T3,T4,T5> type; };
		template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
		struct LuaContext::Tupleizer<std::tuple<T1,T2,T3,T4,T5,T6>>				{ typedef std::tuple<T1,T2,T3,T4,T5,T6> type; };
		template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
		struct LuaContext::Tupleizer<std::tuple<T1,T2,T3,T4,T5,T6,T7>>			{ typedef std::tuple<T1,T2,T3,T4,T5,T6,T7> type; };
		template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
		struct LuaContext::Tupleizer<std::tuple<T1,T2,T3,T4,T5,T6,T7,T8>>		{ typedef std::tuple<T1,T2,T3,T4,T5,T6,T7,T8> type; };
		template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
		struct LuaContext::Tupleizer<std::tuple<T1,T2,T3,T4,T5,T6,T7,T8,T9>>	{ typedef std::tuple<T1,T2,T3,T4,T5,T6,T7,T8,T9> type; };
#	endif
	
}


#endif
